package cn.t.service.device;

import cn.t.constants.CommonConstants;
import cn.t.core.exception.BizException;
import cn.t.core.service.ServiceImpl;
import cn.t.dto.device.DeviceSkuDTO;
import cn.t.core.enums.base.CommonError;
import cn.t.model.device.CargowaySkuDO;
import cn.t.model.device.DeviceDO;
import cn.t.model.user.RoleDeviceDO;
import cn.t.repository.device.DeviceRepository;
import cn.t.request.InventoryReq;
import cn.t.request.QueryDeviceReq;
import cn.t.service.user.RoleDeviceService;
import cn.t.utils.BusinessUtil;
import cn.t.utils.DateUtil;
import cn.t.vo.device.InventoryCargowayVO;
import cn.t.vo.device.InventoryDeviceVO;
import cn.t.vo.device.InventorySkuVO;
import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.toList;

/**
 * 设备表(Device)领域服务
 *
 * @author t
 * @since 2024-05-24 10:19:11
 */
@Slf4j
@Service
@AllArgsConstructor
public class DeviceService extends ServiceImpl<DeviceDO, DeviceRepository> {

    private final CargowaySkuService cargowaySkuService;
    private final RoleDeviceService roleDeviceService;

    // 查询所有主设备，并在内存中统计概览数据
    public Map<String, Long> overview(Long firmId) {
        DeviceDO queryParams = new DeviceDO();
        queryParams.setIsMaster(CommonConstants.ONE);
        if (firmId != -1L) {
            queryParams.setFirmId(firmId);
        }
        List<DeviceDO> allDevices = this.findByModel(queryParams, CommonConstants.ZERO, Integer.MAX_VALUE, null, null);

        if (CollectionUtils.isEmpty(allDevices)) {
            return Collections.emptyMap();
        }

        long deviceNum = allDevices.size();
        long onlineDeviceNum = allDevices.stream()
                .filter(f -> CommonConstants.ONE.equals(f.getStatus()) && (f.getGmtConnect() != null && CommonConstants.ONE.equals(f.getStatus()) && DateUtil.diffTime(f.getGmtConnect(), new Date(), TimeUnit.MINUTES) <= CommonConstants.FIFTEEN)).count();
        long offlineDeviceNum = allDevices.stream()
                .filter(f -> CommonConstants.ONE.equals(f.getStatus()) && (f.getGmtConnect() == null || CommonConstants.ONE.equals(f.getStatus()) && DateUtil.diffTime(f.getGmtConnect(), new Date(), TimeUnit.MINUTES) > CommonConstants.FIFTEEN)).count();
        long banDeviceNum = allDevices.stream().filter(f -> CommonConstants.ZERO.equals(f.getStatus())).count();
        Map<String, Long> overviewDataMap = new HashMap<>();
        overviewDataMap.put("deviceNum", deviceNum);
        overviewDataMap.put("onlineDeviceNum", onlineDeviceNum);
        overviewDataMap.put("offlineDeviceNum", offlineDeviceNum);
        overviewDataMap.put("banDeviceNum", banDeviceNum);
        return overviewDataMap;
    }

    public List<DeviceDO> queryById(Collection<Long> deviceIdSet) {

        return repository.queryById(deviceIdSet);
    }

    public List<DeviceDO> getRecords(List<DeviceDO> allMainDevices, QueryDeviceReq req, Integer pageNo, Integer pageSize) {
        // 过滤
        allMainDevices = filterData(allMainDevices, req);

        // 排序
        if (CommonConstants.ZERO.equals(req.getStockRateSort())) {
            return allMainDevices.stream().sorted((o1, o2) -> {
                // 去除百分号并将字符串转换成双精度浮点数类型
                double d1 = Double.parseDouble(o1.getStockRate().replace("%", ""));
                double d2 = Double.parseDouble(o2.getStockRate().replace("%", ""));
                return Double.compare(d2, d1);
            }).skip((long) (pageNo - 1) * pageSize).limit(pageSize).collect(toList());
        } else if (CommonConstants.ONE.equals(req.getStockRateSort())) {
            // 根据库存率升序排序，并进行分页
            return allMainDevices.stream().sorted((o1, o2) -> {
                // 去除百分号并将字符串转换成双精度浮点数类型
                double d1 = Double.parseDouble(o1.getStockRate().replace("%", ""));
                double d2 = Double.parseDouble(o2.getStockRate().replace("%", ""));
                return Double.compare(d1, d2);
            }).skip((long) (pageNo - 1) * pageSize).limit(pageSize).collect(toList());
        } else {
            return allMainDevices.stream().sorted(Comparator.comparing(DeviceDO::getId).reversed()).skip((long) (pageNo - 1) * pageSize).limit(pageSize).collect(toList());
        }
    }

    public long getTotal(List<DeviceDO> allMainDevices, QueryDeviceReq req) {
        // 过滤
        allMainDevices = filterData(allMainDevices, req);

        return allMainDevices.size();
    }

    public List<DeviceDO> getDevices(List<Long> deviceIds, Long firmId, Date fromDate, Integer status) {
        DeviceDO queryParams = new DeviceDO();
        queryParams.setDeviceIds(deviceIds);
        queryParams.setFirmId(firmId);
        queryParams.setGmtCreatedStart(fromDate);
        queryParams.setStatus(status);
        return this.findByModel(queryParams, CommonConstants.ZERO, Integer.MAX_VALUE, null, null);
    }

    private List<DeviceDO> filterData(List<DeviceDO> allMainDevices, QueryDeviceReq req) {
        List<RoleDeviceDO> roleDeviceDOS = roleDeviceService.findByRoleId(req.getRoleId());

        Map<Long, RoleDeviceDO> roleDeviceMap = roleDeviceDOS.stream().collect(Collectors.toMap(RoleDeviceDO::getDeviceId, Function.identity(), (a, b) -> a));
        allMainDevices.forEach(f -> {
            if (roleDeviceMap.containsKey(f.getId())) {
                f.setRoleDeviceId(roleDeviceMap.get(f.getId()).getId());
            }
        });
        wrapQueryCondition(req, roleDeviceDOS);


        if (req.getDeviceId() != null) {
            allMainDevices = allMainDevices.stream().filter(f -> f.getId().equals(req.getDeviceId())).collect(toList());
        }
        if (StringUtils.hasText(req.getDeviceName())) {
            allMainDevices = allMainDevices.stream().filter(f -> f.getDeviceName().contains(req.getDeviceName())).collect(toList());
        }
        if (StringUtils.hasText(req.getImei())) {
            allMainDevices = allMainDevices.stream().filter(f -> f.getImei().equals(req.getImei())).collect(toList());
        }
        if (req.getFirmId() != null) {
            allMainDevices = allMainDevices.stream().filter(f -> f.getFirmId().equals(req.getFirmId())).collect(toList());
        }
        if (req.getStatus() != null) {
            allMainDevices = allMainDevices.stream().filter(f -> f.getStatus().equals(req.getStatus())).collect(toList());
        }
        if (req.getDeviceType() != null) {
            allMainDevices = allMainDevices.stream().filter(f -> f.getDeviceType().equals(req.getDeviceType())).collect(toList());
        }
        if (req.getDeviceType() != null) {
            allMainDevices = allMainDevices.stream().filter(f -> f.getDeviceType().equals(req.getDeviceType())).collect(toList());
        }
        if (CommonConstants.ONE.equals(req.getOnlineStatus())) {
            allMainDevices = allMainDevices.stream().filter(f -> f.getGmtConnect() != null && DateUtil.diffTime(f.getGmtConnect(), new Date(), TimeUnit.MINUTES) < CommonConstants.FIFTEEN).collect(toList());
        }
        if (CommonConstants.ZERO.equals(req.getOnlineStatus())) {
            allMainDevices = allMainDevices.stream().filter(f -> f.getGmtConnect() == null || DateUtil.diffTime(f.getGmtConnect(), new Date(), TimeUnit.MINUTES) > CommonConstants.FIFTEEN).collect(toList());
        }
        if (!CollectionUtils.isEmpty(req.getFirmIds())) {
            allMainDevices = allMainDevices.stream().filter(f -> req.getFirmIds().contains(f.getFirmId())).collect(toList());
        }
        if (!CollectionUtils.isEmpty(req.getDeviceIds())) {
            allMainDevices = allMainDevices.stream().filter(f -> req.getDeviceIds().contains(f.getId())).collect(toList());
        }
        if (!CollectionUtils.isEmpty(req.getExcludeDeviceIds())) {
            allMainDevices = allMainDevices.stream().filter(f -> !req.getExcludeDeviceIds().contains(f.getId())).collect(toList());
        }
        return allMainDevices;
    }

    private void wrapQueryCondition(QueryDeviceReq req, List<RoleDeviceDO> roleDeviceDOS) {
        // 已绑/未绑sku
        if (req.getRoleId() != null && req.getIsBound() != null) {
            List<Long> boundDeviceIds = roleDeviceDOS.stream().map(RoleDeviceDO::getDeviceId).collect(Collectors.toList());
            if (Boolean.TRUE.equals(req.getIsBound())) {
                req.setDeviceIds(CollectionUtils.isEmpty(boundDeviceIds) ? Lists.newArrayList(-1L) : boundDeviceIds);
            } else if (Boolean.FALSE.equals(req.getIsBound())) {
                req.setExcludeDeviceIds(boundDeviceIds);
            }
        }
    }

    public List<DeviceDO> getDeviceList(String deviceName, Long firmId, List<Long> ids) {
        DeviceDO deviceDO = new DeviceDO();
        deviceDO.setDeviceName(deviceName);
        deviceDO.setFirmId(firmId);
        return repository.getDeviceList(deviceDO, ids);
    }

    // 获取设备可领用商品
    public DeviceSkuDTO getDeviceSku(Long deviceId) {
        DeviceSkuDTO rst = new DeviceSkuDTO();
        // 查询设备信息
        DeviceDO device = this.findById(deviceId);
        if (ObjectUtils.isEmpty(device)) {
            return null;
        }

        rst.setDeviceId(deviceId);
        rst.setDeviceName(device.getDeviceName());
        rst.setFirmId(device.getFirmId());

        // 查询上架商品
        CargowaySkuDO cargowaySkuQueryParams = new CargowaySkuDO();
        cargowaySkuQueryParams.setDeviceId(deviceId);
        List<CargowaySkuDO> cargowaySkuDOS = this.cargowaySkuService.findByModel(cargowaySkuQueryParams, CommonConstants.ZERO, Integer.MAX_VALUE, null, null);

        if (CollectionUtils.isEmpty(cargowaySkuDOS)) {
            return rst;
        }

        List<DeviceSkuDTO.SkuInfo> skuInfos = new ArrayList<>();
        cargowaySkuDOS.forEach(f -> {
            DeviceSkuDTO.SkuInfo skuInfo = new DeviceSkuDTO.SkuInfo();

            // 计算实际库存
            int realInventory = BusinessUtil.calcQty(f);
            skuInfo.setInventory(f.getInventory());
            skuInfo.setRealInventory(realInventory);
            skuInfo.setSkuCode(f.getSkuCode());
            skuInfo.setSkuName(f.getSkuName());
            skuInfo.setCargowayNo(f.getCargowayNo());

            skuInfos.add(skuInfo);
        });

        rst.setSkuInfos(skuInfos);

        return rst;
    }

    // 根据主设备id查询所有相关设备
    public List<DeviceDO> getDevicesByMasterDeviceId(Long deviceId) {

        DeviceDO device = this.findById(deviceId);

        if (ObjectUtils.isEmpty(device)) {
            return Collections.emptyList();
        }

        if (!StringUtils.hasText(device.getImei())) {
            return Lists.newArrayList(device);
        }

        DeviceDO queryParams = new DeviceDO();
        queryParams.setImei(device.getImei());
        return this.findByModel(queryParams, CommonConstants.ZERO, Integer.MAX_VALUE, null, null);
    }

    // 更新连接时间
    public void updateDevice(String imei, Date connectTime) {

        DeviceDO queryParams = new DeviceDO();
        queryParams.setImei(imei);
        List<DeviceDO> deviceDOS = this.findByModel(queryParams, CommonConstants.ZERO, Integer.MAX_VALUE, null, null);
        if (CollectionUtils.isEmpty(deviceDOS)) {
            log.warn("设备不存在");
            return;
        }
        deviceDOS.forEach(f -> f.setGmtConnect(connectTime));
        this.repository.updateBatch(deviceDOS);
    }

    // 批量更新imei
    public void updateDevice(String imei, List<DeviceDO> devices) {

        if (CollectionUtils.isEmpty(devices)) {
            log.warn("设备不存在");
            return;
        }
        devices.forEach(f -> f.setImei(imei));
        this.repository.updateBatch(devices);
    }

    // 根据imei查询设备
    public DeviceDO getDeviceByImei(String imei) {
        DeviceDO queryParams = new DeviceDO();
        queryParams.setImei(imei);
        queryParams.setIsMaster(CommonConstants.ONE);
        List<DeviceDO> devices = this.findByModel(queryParams, CommonConstants.ZERO, Integer.MAX_VALUE, null, null);
        if (CollectionUtils.isEmpty(devices)) {
            String errorMessage = String.format("指定的设备不存在 imei=%s", imei);
            log.error(errorMessage);
            throw new BizException(CommonError.ILLEGAL_PARAMETER, errorMessage);
        }
        return devices.get(0);

    }

    public void updateVersion(DeviceDO deviceDO) {
        repository.updateVersion(deviceDO);
    }

    public List<DeviceDO> getDevicesByImei(String imei) {
        DeviceDO queryParams = new DeviceDO();
        queryParams.setImei(imei);
        return this.findByModel(queryParams, CommonConstants.ZERO, Integer.MAX_VALUE, null, null);
    }

    public List<DeviceDO> getDevicesByFirmId(Long firmId) {
        return repository.getDevicesByFirmId(firmId);
    }

    public List<InventoryDeviceVO> findInventoryDevicePage(Integer pageNo, Integer pageSize, InventoryReq param) {
        return repository.findInventoryDevicePage(pageNo, pageSize, param);
    }

    public Long getInventoryDeviceCount(InventoryReq param) {
        return repository.getInventoryDeviceCount(param);
    }

    public List<InventorySkuVO> findInventorySkuPage(Integer pageNo, Integer pageSize, InventoryReq param) {
        return repository.findInventorySkuPage(pageNo, pageSize, param);
    }

    public Long getInventorySkuCount(InventoryReq param) {
        return repository.getInventorySkuCount(param);
    }

    public List<InventoryCargowayVO> findCargowayPage(Integer pageNo, Integer pageSize, InventoryReq param) {
        return repository.findCargowayPage(pageNo, pageSize, param);
    }

    public Long getInventoryCargowayCount(InventoryReq param) {
        return repository.getInventoryCargowayCount(param);
    }

}
